package c.c.d.s.s.o.a.c.resource.server.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;

/**
 * <b>资源服务器</b> 配置
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-06-02 10:42
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    private static final String RESOURCE_ID = "resource-server";

    /**
     * Description: 为资源服务器配置特定属性, 如 resource-id.<br>
     * Details: 查看 {@link ResourceServerSecurityConfigurer} 的源代码可以知道, 默认情况下,
     * 已经为 {@link ResourceServerSecurityConfigurer} 注入了 {@link OAuth2WebSecurityExpressionHandler}
     *
     * @see org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer#configure(ResourceServerSecurityConfigurer)
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID).stateless(true).tokenServices(remoteTokenServices());
    }

    /**
     * Description: 配置资源的访问规则. 默认情况下, 除了 /oauth/** 之外的所有资源都被保护<br>
     * Details: 默认情况下, {@link OAuth2WebSecurityExpressionHandler} 已经被注入, 形如 {@code http.authorizeRequests().expressionHandler(new OAuth2WebSecurityExpressionHandler())}
     *
     * <pre>
     * protected void configure(HttpSecurity http) throws Exception {
     *     http
     *         .authorizeRequests()
     *             .expressionHandler(new OAuth2WebSecurityExpressionHandler())
     *             .antMatchers("/photos").access("#oauth2.denyOAuthClient() and hasRole('ROLE_USER') or #oauth2.hasScope('read')")
     *             .antMatchers("/photos/trusted/**").access("#oauth2.denyOAuthClient() and hasRole('ROLE_USER') or #oauth2.hasScope('trust')")
     *             .antMatchers("/photos/user/**").access("#oauth2.denyOAuthClient() and hasRole('ROLE_USER') or #oauth2.hasScope('trust')")
     *             .antMatchers("/photos/**").access("#oauth2.denyOAuthClient() and hasRole('ROLE_USER') or #oauth2.hasScope('read')")
     *             .regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*").access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')")
     *             .regexMatchers(HttpMethod.GET, "/oauth/users/.*").access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')")
     *             .regexMatchers(HttpMethod.GET, "/oauth/clients/.*").access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')")
     *
     *         .and().requestMatchers().antMatchers("/photos/**", "/oauth/users/**", "/oauth/clients/**")
     *         .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
     *         .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler())
     *
     *         // CSRF protection is awkward for machine clients
     *         .and().csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/**")).disable()
     *         .apply(new OAuth2ResourceServerConfigurer()).tokenStore(tokenStore).resourceId(SPARK_RESOURCE_ID);
     * }
     * </pre>
     *
     * @see org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer#configure(HttpSecurity)
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests().antMatchers("/resource-server/access").access("#oauth2.hasScope('access')");
    }

    // ~ bean
    // -----------------------------------------------------------------------------------------------------------------

    /**
     * Description: 查询 /check_token 端点获取 access-token 的内容
     */
    private RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:18910/authorization-server/oauth/check_token");
        remoteTokenServices.setClientId("resource-server-id");
        remoteTokenServices.setClientSecret("resource-server-secret");
        return remoteTokenServices;
    }
}
